package org.zn.note.jdk.reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * SpringAOP 只有动态代理，没有静态代理（静态代理ASPECTJ）。关于动态代理：
 * JDK动态代理：只能代理接口而不能代理类 （无论哪种方式，都是动态生成class文件、动态加载）
 * 如果目标对象实现了接口，则Spring用JDK动态代理生成代理类；
 * 如果目标对象没实现接口，则Spring用CGLIB生成代理类。
 * <p>
 * JDK 动态代理  （例子 jdkProxy()）   也生成class ProxyGenerator.generateProxyClass
 * 1、拿到被代理类(ServiceImpl implements Service)的实例 service = new ServiceImpl()
 * 2、创建invocationHandler，即实现 {@link InvocationHandler}
 * 3、创建代理实例 Service serviceProxy = Proxy.newProxyInstance(
 * instance.getClass().getClassLoader(),
 * instance.getClass().getInterfaces(),
 * invocationHandler);
 * Spring注入的是代理实例，我们以为调用的是 service.doService()，实际调用的是 serviceProxy.doService()
 * 当我们调用代理实例的任何方法是，JVM都会拦截然后直接调用 invocationHandler.invoke()
 * 缺点：我们每次实现一个invocationHandler，都是针对特定接口的。针对类的动态代理用CGLIB。
 * <p>
 * CGLIB 动态代理  （例子 cglibProxy()）
 * 相同点：流程上大致一样，都是拦截代理对象的方法，然后执行指定方法。（都是通过生成class实现的）
 * 不同点：不用设置接口。可以代理没有实现接口的类。
 * (CGLIB生成的class文件直接放在一个byte[]里面，然后用ClassLoad加载)
 *
 * 在1.8以后，jdk动态代理比cglib运行效率高。以前是cglib高。
 */
public class DynamicProxy {

    // JAVA动态代理
    private static void jdkProxy() {
        // 我有一个IService的对象
        IService merchantService = new MerchantService();
        // 我要拿到这个对象的代理（在Spring里，注入的就是代理类）
        IService merchantServiceProxy = buildIServiceProxy(merchantService);
        // 调用方法
        merchantServiceProxy.doService(123);
    }

    private static IService buildIServiceProxy(IService instance) {
        // Step 2 创建通知（具体行为在这里实现）
        InvocationHandler invocationHandler = new MyInvocationHandler(instance);
        // Step 3 创建代理对象
        IService $proxy = (IService) Proxy.newProxyInstance(
                instance.getClass().getClassLoader(),
                instance.getClass().getInterfaces(),
                invocationHandler);
        return $proxy;
    }

    static class MyInvocationHandler implements InvocationHandler {
        private IService service;

        MyInvocationHandler(IService service) {
            this.service = service;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//            System.out.println("开始执行 " + method.getName());
            Object result = method.invoke(this.service, args);
//            System.out.println("执行完毕 " + method.getName());
            return result;
        }
    }

    // CGLIB 动态代理
    private static void cglibProxy() {
        // 被代理对象实例
        MerchantService merchantService = new MerchantService();
//        MerchantService merchantServiceProxy = (MerchantService) buildProxyByCglib(merchantService);
        MerchantService merchantServiceProxy = null;
        merchantServiceProxy.doService(123);
    }

//    import org.springframework.cglib.proxy.Enhancer;
//import org.springframework.cglib.proxy.MethodInterceptor;
//import org.springframework.cglib.proxy.MethodProxy;
//
//    private static Object buildProxyByCglib(Object instance) {
//        Enhancer enhancer = new Enhancer();
//        enhancer.setSuperclass(instance.getClass()); // 指定父类, CGLIB创建的代理类都是这个类的子类
//        enhancer.setCallback(new MethodInterceptor() {
//            @Override
//            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
////                System.out.println("开始执行 " + method.getName());
//                Object ret = methodProxy.invokeSuper(o, objects);
////                System.out.println("执行完毕 " + method.getName());
//                return ret;
//            }
//        });
//        return enhancer.create();
//    }

    public static void main(String[] args) {
//        jdkProxy();
//        cglibProxy();
        ProxyPerformanceTest.process();
    }

    // 性能测试
    static class ProxyPerformanceTest {
        static void process() {
            // 创建测试对象
            IService nativeTest = new MerchantService();
            IService dynamicProxy = buildIServiceProxy(nativeTest);
//            IService cglibProxy = (IService) buildProxyByCglib(nativeTest);
            IService cglibProxy = null;
            // 预热一下
            int preRunCount = 10000;
            runWithoutMonitor(nativeTest, preRunCount);
            runWithoutMonitor(cglibProxy, preRunCount);
            runWithoutMonitor(dynamicProxy, preRunCount);

            //执行测试
            Map<String, IService> tests = new LinkedHashMap<String, IService>();
            tests.put("Native   ", nativeTest);
            tests.put("Dynamic  ", dynamicProxy);
            tests.put("Cglib    ", cglibProxy);
            int repeatCount = 3;
            int runCount = 1000000;
            runTest(repeatCount, runCount, tests);
            runCount = 50000000;
            runTest(repeatCount, runCount, tests);
        }

        private static void runTest(int repeatCount, int runCount, Map<String, IService> tests) {
            System.out.println(
                    String.format("\n===== run test : [repeatCount=%s] [runCount=%s] [java.version=%s] =====",
                            repeatCount, runCount, System.getProperty("java.version")));
            for (int i = 0; i < repeatCount; i++) {
                System.out.println(String.format("\n--------- test : [%s] ---------", (i + 1)));
                for (String key : tests.keySet()) {
                    runWithMonitor(tests.get(key), runCount, key);
                }
            }
        }

        private static void runWithoutMonitor(IService target, int runCount) {
            for (int i = 0; i < runCount; i++) {
                target.doService(i);
            }
        }

        private static void runWithMonitor(IService target, int runCount, String tag) {
            long start = System.currentTimeMillis();
            for (int i = 0; i < runCount; i++) {
                target.doService(i);
            }
            long end = System.currentTimeMillis();
            System.out.println("[" + tag + "] Total Time:" + (end - start) + "ms");
        }
    }
}

// 接口
interface IService {
    int doService(int i);
}

// 实现类
class MerchantService implements IService {
    @Override
    public int doService(int i) {
//        System.out.println("this is MerchantService");
        return i + 1;
    }
}